/**
 * 手写promise
 */

//首先promise中拥有三个状态
const PENDING = "pending", //等待中
  FULLFILLED = "fullfilled", //请求完成
  REJECTED = "rejected"; //请求失败

class MyPromise {
  //状态
  status = PENDING;

  //成功的回调函数
  successCallback = [];

  //失败的回调函数
  failCallback = [];

  //成功的返回值
  value = undefined;

  //失败的错误
  error = undefined;

  //构造函数
  constructor(exector) {
    try {
      exector(this.resolve, this.reject);
    } catch (error) {
      this.reject(error);
    }
  }
  //把状态置于成功
  resolve = value => {
    //判断当前的状态是否是等待中 如果不是等待中 直接返回
    if (this.status !== PENDING) return;
    this.status = FULLFILLED;
    this.value = value;
    //循环取出成功的回调函数并执行
    while (this.successCallback.length) {
      this.successCallback.shift()();
    }
  };

  //把状态置于失败
  reject = error => {
    //判断当前的状态是否是等待中 如果不是等待中 直接返回
    if (this.status !== PENDING) return;
    this.status = REJECTED;
    this.error = error;

    //循环取出失败的回调函数并执行
    while (this.failCallback.length) {
      this.failCallback.shift()();
    }
  };

  //then方法
  then(successCb, failCb) {
    //如果存在回调,并且回调的类型是function的话,就使用这个回调,否则就是使用默认的回调
    successCb =
      successCb && typeof successCb === "function" ? successCb : value => value;
    failCb =
      failCb && typeof failCb === "function"
        ? failCb
        : () => {
            throw this.error;
          };

    //通过返回promise来实现链式调用
    let promise = new MyPromise((resolve, reject) => {
      if (this.status === FULLFILLED) {
        //通过settimeout函数来让下面的代码变成异步代码 这样在执行的时候就可以获取到promise对象了
        setTimeout(() => {
          //捕获错误
          try {
            let result = successCb(this.value);
            return resolvePromise(promise, result, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let error = failCb(this.error);
            return resolvePromise(promise, error, resolve, reject);
          } catch (error) {
            reject(error);
          }
        }, 0);
      } else {
        //当为等待的状态中的时候 把异步函数的回调装入数组中，后面通过reslove或者reject来触发
        this.successCallback.push(() => {
          try {
            let result = successCb(this.value);
            return resolvePromise(promise, result, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });

        this.failCallback.push(() => {
          try {
            let error = failCb(this.error);
            return resolvePromise(promise, error, resolve, reject);
          } catch (error) {
            reject(error);
          }
        });
      }
    });

    return promise;
  }

  //resolve方法
  static resolve(value) {
    //如果本身是MyPromise的对象 就返回自己本身
    if (value instanceof MyPromise) return value;
    return new MyPromise(resolve => {
      resolve(value);
    });
  }

  //reject方法
  static reject(error) {
    if (error instanceof MyPromise) return error;
    return new MyPromise((undefined, reject) => {
      reject(error);
    });
  }

  //   all方法
  static all(array) {
    let result = [];

    //计数器  用于计算当前array里面的是否全都返回内容了
    let count = 0;

    return new MyPromise((reslove, reject) => {
      //赋值  并判断当前的值是否全都获取到了
      function setData(index, value) {
        result[index] = value;
        count++;
        if (count === array.length) {
          reslove(result);
        }
      }
      for (let i = 0; i < array.length; i++) {
        let current = array[i];
        //判断是否是MyPromise对象
        if (current instanceof MyPromise) {
          current.then(
            value => {
              setData(i, value);
            },
            error => reject(error)
          );
        } else {
          setData(i, current);
        }
      }
    });
  }

  //race方法
  static race(array) {
    return new MyPromise((reslove, reject) => {
      //循环  只要有一个返回就立即返回值
      for (let i = 0; i < array.length; i++) {
        const current = array[i];
        //判断是否是MyPromise对象
        if (current instanceof MyPromise) {
          current.then(
            value => reslove(value),
            error => reject(error)
          );
        } else {
          reslove(current);
        }
      }
    });
  }

  //finally方法
  finally(callBack) {
    //通过返回promise对象让后面可以继续调用then方法
    return this.then(
      value => {
        return MyPromise.reslove(callBack()).then(() => value);
      },
      error => {
        return MyPromise.reject(callBack()).then(() => {
          throw error;
        });
      }
    );
  }

  //catch方法
  catch(callBack) {
    //捕获异常
    return this.then(undefined, error => {
      callBack(error);
    });
  }
}

//判断当前的数据类型 并根据不同的类型做不同的处理
function resolvePromise(promise, result, reslove, reject) {
  //判断当前的promise和要调用的promise是否是同一个 如果是同一个的话就抛出错误
  if (promise === result) {
    return reject(new TypeError("不能循环调用自己"));
  }
  //判断当前的类型是值还是promise对象 如果是promise对象的话就调用then方法
  if (result instanceof MyPromise) {
    return result.then(reslove, reject);
  } else {
    return reslove(result);
  }
}

module.exports = MyPromise;
